#include "shm.h"
#include <errno.h>
#include <cstring>
#include <stdio.h>

using namespace std;

int write_shm::write_status(int st)
{
	if(!has_init()){
		cout <<"write_shm didn't init" << endl;
		return -1;
	}
	void *addr = get_memory();
	if(addr == NULL)
		return -1;
	
	cout << "addr 0:" << static_cast<const void *>(addr) 
		<< "; int:" << *(int *)addr << endl;
	*(int *)addr = st;
	cout << "addr 1:" << static_cast<const void *>(addr) 
		<< "; int:" << *(int *)addr << endl;
}

int write_shm::write_info(int offsize,const void *src, int len)
{
	if(offsize < 0 || len < 0 || len > size()){
		cout << "write_info arg error" << endl;
		return -1;
	}
	if(!has_init()){
		cout <<"write_shm didn't init" << endl;
		return -1;
	}
	char *addr = (char *)get_memory();
	if(addr == NULL)
		return -1;
	memcpy(addr+offsize, src, len);
	return 0;
}

int write_shm::init(int serial, int size){
	if(serial <= 0){
		cout << "serial error"  << endl;
		return -1;
	}
	if(size <= 0){
		cout << "size error" << endl;
		return -1;
	}

	//int shmget(key_t key,size_t size,int shmflg);
	//int flag = shmget(ftok("./writer", 1), size, IPC_CREAT | 0644);
	int id = shmget(get_shm_key()+serial, size, IPC_CREAT | IPC_EXCL | 0644);
	if(id == -1){
		//cout << __FILE__ << ":" << __LINE__ << " Critical error" << endl;
		if(errno = EEXIST){
			cout << "write init the shm is exist" << endl;
			return -2;
		}else{
			cout << "write init shmget error" << endl;
			return -1;
		}
	}
	set_shm_id(id);
	void *addr = shmat(id, NULL, 0);
	if(addr == NULL || addr == (void *)(-1)){
		cout << "write init shmat error" << endl;
		return -1;
	}
	memset(addr, 0, size);
	set_memory(addr);
	set_size(size);
	cout << "write_shm init success" << endl;
	set_init(true);
	return 0;
}

int write_shm::release(){
	if(has_init()){
		cout << "going to release shm_id: " << get_shm_id() << ", addr:" << static_cast<const void *>(get_memory()) << endl;
		shmdt(get_memory());
		shmctl(get_shm_id(), IPC_RMID, NULL);
		return 0;
	}
	cout << "write_shm release error" << endl;
	return -1;
}

int read_shm::read_status()
{
	if(!has_init()){
		cout <<"can't read" << endl;
		return -1;
	}
	void *addr = get_memory();
	if(addr == NULL)
		return -1;
	
	int st = 0;
	cout << "addr 0:" << static_cast<const void *>(addr) 
		<< "; int:" << st << endl;
	st = *(int *)addr;
	//*(int *)addr = st;
	cout << "addr 1:" << static_cast<const void *>(addr) 
		<< "; int:" << st << endl;
}

int read_shm::read_info(int offsize, int len)
{
	if(offsize < 0 || len < 0 || len > size()){
		cout << "read_info arg error" << endl;
		return -1;
	}
	if(!has_init()){
		cout <<"read_shm didn't init" << endl;
		return -1;
	}
	char *addr = (char *)get_memory();
	if(addr == NULL)
		return -1;
	cout << "read info: ";
	for(int i = 0; i < len; i++)
		printf("%c", *(addr + offsize + i));
	cout << endl;
	//memcpy(addr+offsize, src, len);
	return 0;
}

int read_shm::copy_info(void *dest, int offsize, int len)
{
	if(offsize < 0 || len < 0 || len > size() || dest == NULL){
		cout << "read_info arg error" << endl;
		return -1;
	}
	if(!has_init()){
		cout <<"read_shm didn't init" << endl;
		return -1;
	}
	char *addr = (char *)get_memory();
	if(addr == NULL)
		return -1;
	memcpy(dest, addr+offsize, len);
	return 0;
}

int read_shm::init(int serial, int size){
	if(serial<= 0){
		cout << "serial error" << endl;
		return -1;
	}
	if(size <= 0){
		cout << "size error" << endl;
		return -1;
	}
	int id = shmget(get_shm_key()+serial, 0, SHM_RDONLY);
	if(id == -1){
		cout << "read_shm init() error" << endl;
		return -1;
	}
	set_shm_id(id);
	void *addr = shmat(id, NULL, 0);
	//every time, the addr is diffent, because the addr is virtual address of process
	if(addr == NULL || addr == (void *)(-1)){
		cout << "read_shm shmat() error" << endl;
		return -1;
	}
	set_memory(addr);
	shmid_ds buf;
	int ret = shmctl(id, IPC_STAT, &buf);
	if(ret < 0){
		cout << "read_shm get shm info err" << endl;
		return -1;
	}
	set_size(buf.shm_segsz);

	cout << "read_shm init success" << endl;
	set_init(true);
	return 0;
}


int read_shm::release(){
	if(has_init()){
		shmdt(get_memory());
		return 0;
	}
	cout << "write_shm release error" << endl;
	return -1;
}

void shm_base::status()
{
	if(has_init()){
		cout << "init success, shm_id: " << get_shm_id() << "; size: " << size() <<", addr:" << static_cast<const void *>(get_memory()) << endl;
	}
	else
		cout << "intit failed" << endl;
}


